package deepr.src.graph;

import deepr.anno.Type;
import deepr.core.DisposeContext;
import deepr.db.Neo4jDb;
import deepr.exception.DeeprException;
import deepr.src.DeeprMapper;
import lombok.Data;
import lombok.EqualsAndHashCode;
import lombok.extern.slf4j.Slf4j;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Component;

import java.util.HashMap;
import java.util.HashSet;
import java.util.Map;

@EqualsAndHashCode(callSuper = true)
@Data
@Component
@Slf4j
public class GraphDisposeContext extends DisposeContext {

  @Autowired
  private Neo4jDb neo4jDb;

  private String optional = "";
  private String targetObject;
  private HashSet<String> appear = new HashSet<>();
  private Map<String, StringBuilder> matchWith = new HashMap<>();
  private StringBuilder matchSb = new StringBuilder();
  private StringBuilder withForeignSb = new StringBuilder();

  public void rest() {
    optional = "";
    targetObject = null;
    appear = new HashSet<>();
    matchWith = new HashMap<>();
    matchSb = new StringBuilder();
    withForeignSb = new StringBuilder();
    super.reset();
  }

  public void appendMatchWith(String matchObject, String property, String withStr) {
    StringBuilder sb = matchWith.getOrDefault(matchObject, new StringBuilder());
    if (sb.length() > 0)
      sb.append(",");
    sb.append(property).append(":{").append(withStr).append("}");
    matchWith.put(matchObject, sb);
  }

  public void setOptionalModel() {
    this.optional = "OPTIONAL ";
  }

  public void setMatchModel() {
    this.optional = "";
  }

  public void appendMatchWith(String matchObject, String withStr) {
    StringBuilder sb = matchWith.getOrDefault(matchObject, new StringBuilder());
    if (sb.length() > 0)
      sb.append(",");
    sb.append(withStr);
    matchWith.put(matchObject, sb);
  }

  public StringBuilder combineMatchWith() {
    StringBuilder sb = new StringBuilder();
    if (matchWith.size() > 0)
      sb.append("{");
    for (Map.Entry<String, StringBuilder> entry : matchWith.entrySet()) {
      String object = entry.getKey();
      StringBuilder propertyWith = entry.getValue();
      if (sb.length() > 1)
        sb.append(",\n");
      if (object.equals(targetObject)) {
        sb.append(propertyWith);
        continue;
      }
      if (getSchemaData().getPropertyData(object).getType() == Type.LIST) {
        sb.append(object).append(": CASE WHEN NOT ").append(object).append(" IS NULL THEN COLLECT({").append(propertyWith).append("}) ELSE NULL END");
        continue;
      }
      sb.append(object).append(": CASE WHEN NOT ").append(object).append(" IS NULL THEN {").append(propertyWith).append("} ELSE NULL END");
    }
    return sb.append("}");
  }

  public void addAppear(String object) {
    this.appear.add(object);
  }

  public boolean appearContains(String object) {
    return appear.contains(object);
  }

  @Override
  public DeeprMapper getDeeprMapper() {
    return neo4jDb;
  }

}
